package com.xiaoyu.dp;

import java.util.Arrays;

/**
 * @program: DS_and_A
 * @description: 接雨水
 *
 * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图，计算按此排列的柱子，下雨之后能接多少雨水。
 * 输入：height = [0,1,0,2,1,0,1,3,2,1,2,1]
 * 输出：6
 * 解释：上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图，
 * 在这种情况下，可以接 6 个单位的雨水（蓝色部分表示雨水）。
 *
 * @author: YuWenYi
 * @create: 2021-05-31 10:26
 **/
public class ReceiveRainwater_42 {

    //解法一:暴力求解
    public static int trap(int[] height) {
        int res = 0;
        for (int i = 0; i < height.length; i++) {
            int leftHeight = 0,rightHeight = 0;

            for (int j = 0; j <= i; j++) {
                leftHeight = Math.max(leftHeight, height[j]);
            }

            for (int j = i; j < height.length; j++) {
                rightHeight = Math.max(rightHeight, height[j]);
            }

            res += Math.min(leftHeight, rightHeight) - height[i];
        }
        return res;
    }

    //方法二:使用动态规划求解,在暴力的基础上记录每个柱子的左右最高值
    public static int trap1(int[] height) {
        int res = 0;
        int len = height.length;
        if (len == 0) return res;
        //dp[i][0]-->第i根柱子的左边最高点, dp[i][1] --> 第i根柱子的右边最高点
        //dp公式为:dp[i][0] = max(dp[i-1][0],height[i])和dp[i][1] = max(dp[i+1][1],height[i])
        int[][] dp = new int[len][2];
        dp[0][0] = height[0];
        for (int i = 1; i < len; i++) {
            dp[i][0] = Math.max(dp[i-1][0],height[i]);
        }

        dp[len-1][1] = height[len-1];
        for (int i = len - 2; i >= 0; i--) {
            dp[i][1] = Math.max(dp[i+1][1],height[i]);
        }

        for (int i = 0; i < height.length; i++) {
            res += Math.min(dp[i][0],dp[i][1]) - height[i];
        }
        return res;
    }

    //方法三:DP+双指针,省空间大法
    /*
    * 只要右边有比左边长的或者等于的,左边就可以去统计长度
    * 同理只要左边有比右边长或者等于的,右边就可以去统计长度
    * 最后两个指针相遇,就统计完了
    *
    * 这个思路和装水容量的思路很相似
    * */
    public static int trap2(int[] height) {
        int res = 0,left = 0,right = height.length-1;
        int leftMax = 0,rightMax = 0;
        while (left <= right){
            if (leftMax <= rightMax){
                leftMax = Math.max(leftMax, height[left]);
                res += leftMax - height[left++];
            }else {
                rightMax = Math.max(rightMax, height[right]);
                res += rightMax - height[right--];
            }
        }
        return res;
    }

    /*
    *  public int trap(int[] height) {
        Stack<Integer> stack = new Stack<>();
        int res = 0;
        // 遍历每个柱体
        for (int i = 0; i < height.length; i++) {
           while (!stack.isEmpty() && height[stack.peek()] < height[i]) {
                int bottomIdx = stack.pop();
                // 如果栈顶元素一直相等，那么全都pop出去，只留第一个。
                while (!stack.isEmpty() && height[stack.peek()] == height[bottomIdx]) {
                    stack.pop();
                }
                if (!stack.isEmpty()) {
                    // stack.peek()是此次接住的雨水的左边界的位置，右边界是当前的柱体，即i。
                    // Math.min(height[stack.peek()], height[i]) 是左右柱子高度的min，减去height[bottomIdx]就是雨水的高度。
                    // i - stack.peek() - 1 是雨水的宽度。
                    res += (Math.min(height[stack.peek()], height[i]) - height[bottomIdx]) * (i - stack.peek() - 1);
                }
            }
            stack.push(i);
        }
        return res;
    }
    *
    * // 采用单调递减栈
    public int trap(int[] height) {
        int res = 0;
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < height.length; i++) {
            while (!stack.isEmpty() && height[stack.peek()] < height[i]) {
                int curr = stack.peek();
                stack.pop();
                if (stack.isEmpty()) {
                    break;
                }
                int left = stack.peek();
                int right = i;
                int h = Math.min(height[left], height[right]) - height[curr];
                res += (right - left - 1) * h;
            }
            stack.push(i);
        }
        return res;
    }
    *
    * */

    public static void main(String[] args) {
        int[] height = {0,1,0,2,1,0,1,3,2,1,2,1};
        System.out.println(trap2(height));
    }
}
